﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Data.Entity;
using System.Web;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;

using Subgurim.Controles;

namespace CorreoSocialWeb.Models
{
    [System.ComponentModel.DataAnnotations.Schema.Table("RutasDeEnvio")]
    public class RutaDeEnvio
    {
        [Key]
        public int Id { get; set; }

        [System.ComponentModel.DataAnnotations.Schema.ForeignKey("Envio")]
	    public int EnvioId	{ get; set; }

        [System.ComponentModel.DataAnnotations.Schema.ForeignKey("Ruta")]
    	public int RutaId	{ get; set; }

        [System.ComponentModel.DataAnnotations.Schema.ForeignKey("CalificacionMensajeroRemitente")]
        public int? CalificacionMensajeroRemitenteId	{ get; set; }

        [System.ComponentModel.DataAnnotations.Schema.ForeignKey("CalificacionRemitenteMensajero")]
        public int? CalificacionRemitenteMensajeroId { get; set; }

        [Association("Ruta", "RutaId", "Id")]
        public Ruta Ruta { get; set; }

        [Association("Envio", "EnvioId", "Id")]
        public Envio Envio { get; set; }

        [Association("CalificacionMensajeroRemitente", "CalificacionMensajeroRemitenteId", "Id")]
        public Calificacion CalificacionMensajeroRemitente { get; set; }

        [Association("CalificacionRemitenteMensajero", "CalificacionRemitenteMensajeroId", "Id")]
        public Calificacion CalificacionRemitenteMensajero { get; set; }

        public string ComentariosSolicitud { get; set; }
        public string ComentariosRechazo { get; set; }

        public int EstadoId { get; set; }

        [NotMapped]
        public string EstadoString
        {
            get
            {
                return new EnumStringHelper().Get(this.EstadoEnu);
            }
        }

        [NotMapped]
        public bool EsNuevo
        {
            get
            {
                return this.Id < 1;
            }
        }

        [NotMapped]
        public EstadoDeRutaDeEnvio EstadoEnu
        {
            get
            {
                return (EstadoDeRutaDeEnvio)this.EstadoId;
            }
            set
            {
                this.EstadoId = (int) value;
            }
        }

        [NotMapped]
        public int? Progreso
        {
            get
            {
                if (this.EstadoEnu != EstadoDeRutaDeEnvio.Aceptada)
                    return null;

                if (this.Ruta == null)
                    return null;

                if (this.Ruta.Usuario == null)
                    return null;

                if (!this.Ruta.Usuario.TienePosicionActualizadaA(this.FHAceptacion.Value))
                    return null;

                if (this.Ruta.FueraDeRuta)
                    return null;

                var progresoDecimal = 1 - this.Ruta.GetDistanciaPosicionUsuarioADestino().Value / this.Ruta.GetDistanciaOrigenDestino();

                return (int) (progresoDecimal * 100);
            }
        }

        [NotMapped]
        public string ProgresoString
        {
            get
            {
                if (this.EstadoEnu != EstadoDeRutaDeEnvio.Aceptada)
                    return "N/A";

                if (this.Ruta == null)
                    return "(ruta no encontrada)";

                if (this.Ruta.Usuario == null)
                    return "(usuario de ruta no encontrado)";

                if (!this.Ruta.Usuario.TienePosicionActualizadaA(this.FHAceptacion.Value))
                    return "Esperando Informe de Posición";

                if (this.Ruta.FueraDeRuta)
                    return "Fuera de Ruta";

                return string.Format("{0} %", this.Progreso.Value);
            }
        }

        public static void Confirmar(int id, string comentarios)
        {
            CorreoSocialDbContext db = new CorreoSocialDbContext();

            var rutaDeEnvio = db.RutasDeEnvio
             .Include(x => x.Envio)
             .Include(x => x.Envio.Usuario)
             .Include(x => x.Ruta)
             .Include(x => x.Ruta.Usuario)
             .First(x => x.Id.Equals(id));

            rutaDeEnvio.ComentariosRechazo = comentarios;
            rutaDeEnvio.EstadoEnu = EstadoDeRutaDeEnvio.Aceptada;
            rutaDeEnvio.Envio.EstadoEnu = EstadoDeEnvioEnu.DespachoPendiente;
            rutaDeEnvio.FHAceptacion = DateTime.Now;

            rutaDeEnvio.Envio.Usuario.MandarMailSolicitudRutaAceptada(rutaDeEnvio);

            rutaDeEnvio.Ruta.Usuario.MandarMailDatosDeContactoRemitente(rutaDeEnvio);

            db.SaveChanges();
        }

        public static void Rechazar(int id, string comentarios)
        {
            CorreoSocialDbContext db = new CorreoSocialDbContext();

            var rutaDeEnvio = db.RutasDeEnvio
             .Include(x=>x.Envio)
             .Include(x=>x.Envio.Usuario)
             .Include(x=>x.Ruta)
             .First(x => x.Id.Equals(id));

            rutaDeEnvio.ComentariosRechazo = comentarios;
            rutaDeEnvio.EstadoEnu = EstadoDeRutaDeEnvio.Rechazada;
            rutaDeEnvio.Envio.EstadoEnu = EstadoDeEnvioEnu.SeleccionandoMensajero;

            rutaDeEnvio.Envio.Usuario.MandarMailSolicitudRutaRechazada(rutaDeEnvio);

            db.SaveChanges();
        }

        private static RutaDeEnvio Nueva(int envioId, int rutaId, string comentarios)
        { 
            RutaDeEnvio item = new RutaDeEnvio();

            item.Id = 0;
            item.EstadoEnu = EstadoDeRutaDeEnvio.Solicitada;

            item.RutaId = rutaId;
            item.EnvioId = envioId;
            item.ComentariosSolicitud = comentarios;

            return item;
        }

        public static RutaDeEnvio InsertarNueva(int envioId, int rutaId, string comentarios)
        {
            CorreoSocialDbContext db = new CorreoSocialDbContext();

            Envio envio = db.Envios.First(x=>x.Id.Equals(envioId));

            if (envio == null)
                throw new Exception("Envío no encontrado.");

            if (!envio.PuedeBuscarRuta)
                throw new Exception("Este envío no puede seleccionar ruta.");

            envio.EstadoEnu = EstadoDeEnvioEnu.MensajeroSolicitado;

            RutaDeEnvio item = Nueva(envioId, rutaId, comentarios);

            db.RutasDeEnvio.Add(item);

            db.SaveChanges();

            // vuelvo a traerla con datos adicionales
            // para que pueda armar el mail de confirmación
            item = new CorreoSocialDbContext().RutasDeEnvio
                .Include(x=> x.Envio)
                .Include(x=> x.Envio.Usuario)
                .Include(x=> x.Ruta)
                .Include(x=> x.Ruta.Usuario)
                .First(x => x.Id.Equals(item.Id));

            return item;
        }

        public static RutaDeEnvio GetParaConfirmar(int rutaDeEnvioId)
        {
            return new CorreoSocialDbContext().RutasDeEnvio
                .Include("Ruta")
                .Include("Ruta.Usuario")
                .Include("Ruta.TamanoDePaquete")
                .Include("Envio")
                .Include("Envio.Usuario")
                .Include("Envio.TamanoDePaquete").FirstOrDefault(x => x.Id.Equals(rutaDeEnvioId));
        }

        public DateTime? FHAceptacion { get; set; }

        public static void Finalizar(int rutaDeEnvioId, bool usuarioEsMensajero, bool cancelando, PuntajeDeCalificacion puntaje, string comentarios)
        {
            CorreoSocialDbContext db = new CorreoSocialDbContext();

            // busco la ruta
            RutaDeEnvio rutaDeEnvio = db.RutasDeEnvio
                .Include(x =>x.Ruta)
                .Include(x=>x.Ruta.Usuario)
                //.Include("Ruta.TamanoDePaquete")
                .Include(x => x.Envio)
                .Include(x => x.Envio.Usuario)
                .Include(x => x.CalificacionRemitenteMensajero)
                .Include(x => x.CalificacionRemitenteMensajero.Calificado)
                .Include(x => x.CalificacionRemitenteMensajero.Calificador)
                .Include(x => x.CalificacionMensajeroRemitente)
                .Include(x => x.CalificacionMensajeroRemitente.Calificado)
                .Include(x => x.CalificacionMensajeroRemitente.Calificador)
                //.Include("Envio.TamanoDePaquete")
                .FirstOrDefault(x => x.Id.Equals(rutaDeEnvioId));

            // guardo calificador y calificado
            var calificado = usuarioEsMensajero ? rutaDeEnvio.Remitente : rutaDeEnvio.Mensajero;
            var calificador = usuarioEsMensajero ? rutaDeEnvio.Mensajero : rutaDeEnvio.Remitente;

            // creo una calificación
            Calificacion c= new Calificacion();
            c.PuntajeEnu = puntaje;
            c.Comentarios = comentarios;
            c.UsuarioCalificadorId = calificador.Id;
            c.UsuarioCalificadoId = calificado.Id;
            c.Calificador = calificador;
            c.Calificado = calificado;

            // guardo estado y calificacion en ruta o envio según corresponda
            if (usuarioEsMensajero)
            {
                if (!rutaDeEnvio.SinCalificacionMensajeroRemitente || !rutaDeEnvio.PuedeCalificarMensajero)
                    throw new Exception("Ruta de envío no admite calificación.");

                rutaDeEnvio.CalificacionMensajeroRemitente = c;
                rutaDeEnvio.EstadoEnu = cancelando ? EstadoDeRutaDeEnvio.Cancelada : EstadoDeRutaDeEnvio.Finalizada;
            }
            else
            {
                if (!rutaDeEnvio.SinCalificacionRemitenteMensajero || !rutaDeEnvio.PuedeCalificarRemitente)
                    throw new Exception("Ruta de envío ya calificada o en estado inválido.");

                rutaDeEnvio.CalificacionRemitenteMensajero = c;
                rutaDeEnvio.Envio.EstadoEnu = cancelando ? EstadoDeEnvioEnu.Cancelado : EstadoDeEnvioEnu.EnDestino;
            }

            List<Calificacion> calificaciones = db.Calificaciones.Where(x=>x.UsuarioCalificadoId.Equals(calificado.Id)).ToList();

            int total = calificaciones.Sum(x=> (int)x.Puntaje) + (int) puntaje;

            var promedio = total / (calificaciones.Count() + 1);

            calificado.Reputacion = promedio;

            // mando mail siempre al calificador indicando la operación que acaba de hacer
            if (usuarioEsMensajero)
                calificador.MandarMailEnvioFinalizadoMensajero(rutaDeEnvio);
            else
                calificador.MandarMailEnvioFinalizadoRemitente(rutaDeEnvio);

            // mando al calificado cuando se hizo la última calificacion 
            if (!rutaDeEnvio.TieneCalificacionesPendientes)
            {
                rutaDeEnvio.CalificacionMensajeroRemitente.EnviarMailCalificado();
                rutaDeEnvio.CalificacionRemitenteMensajero.EnviarMailCalificado();
            }

            db.SaveChanges();
        }

        /* ojo, me tengo que traer las relaciones para referir esta propiedad */
        public Usuario Mensajero
        {
            get
            {
                return this.Ruta.Usuario;
            }
        }

        /* ojo, me tengo que traer las relaciones para referir esta propiedad */
        public Usuario Remitente
        {
            get
            {
                return this.Envio.Usuario;
            }
        }

        public bool SinCalificacionMensajeroRemitente
        {
            get
            {
                return this.CalificacionMensajeroRemitenteId == null;
            }
        }

        public bool SinCalificacionRemitenteMensajero
        {
            get
            {
                return this.CalificacionRemitenteMensajeroId == null;
            }
        }

        public bool PuedeCalificarRemitente
        { 
            get
            {
                return this.Envio.EstadoEnu == EstadoDeEnvioEnu.EnTransito
                    || this.Envio.EstadoEnu == EstadoDeEnvioEnu.DespachoPendiente;
            }
        }

        public bool PuedeCalificarMensajero
        {
            get
            {
                return this.EstadoEnu == EstadoDeRutaDeEnvio.Aceptada;
            }
        }

        public bool TieneCalificacionesPendientes
        {
            get
            {
                return this.CalificacionRemitenteMensajero == null
                        ||
                        this.CalificacionMensajeroRemitente == null;
            }
        }

        [NotMapped]
        public bool PuedeAceptarRechazar 
        {
            get
            {
                return this.EstadoEnu == EstadoDeRutaDeEnvio.Solicitada;
            }
        }

        [NotMapped]
        public bool PuedeCancelarEnTransito 
        {
            get
            {
                return this.EstadoEnu == EstadoDeRutaDeEnvio.Aceptada
                    && this.Envio != null
                    && (this.Envio.EstadoEnu == EstadoDeEnvioEnu.EnTransito || this.Envio.EstadoEnu == EstadoDeEnvioEnu.EnDestino || this.Envio.EstadoEnu == EstadoDeEnvioEnu.Cancelado);
            }
        }

        [NotMapped]
        public bool PuedeMarcarEntregado
        {
            get
            {
                return this.EstadoEnu == EstadoDeRutaDeEnvio.Aceptada
                    && this.Envio != null
                    && (this.Envio.EstadoEnu == EstadoDeEnvioEnu.EnTransito || this.Envio.EstadoEnu == EstadoDeEnvioEnu.EnDestino || this.Envio.EstadoEnu == EstadoDeEnvioEnu.Cancelado);
            }
        }

        [NotMapped]
        public bool EstaPendiente
        {
            get
            {
                return 
                    this.EstadoEnu != EstadoDeRutaDeEnvio.Finalizada 
                    && this.EstadoEnu != EstadoDeRutaDeEnvio.Rechazada
                    && this.EstadoEnu != EstadoDeRutaDeEnvio.Cancelada;
            }
        }
    }
}

